package com.xiangxiao.rpan.authority.oauth;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.xiangxiao.rpan.authority.constant.GlobalConsts;
import com.xiangxiao.rpan.authority.dao.UserMapper;
import com.xiangxiao.rpan.authority.model.*;
import com.xiangxiao.rpan.authority.service.IRoleService;
import com.xiangxiao.rpan.authority.service.UserService;
import com.xiangxiao.rpan.authority.tree.TreeNode;
import com.xiangxiao.rpan.authority.vo.UserVo;
import com.xiangxiao.rpan.utils.DateUtil;
import com.xiangxiao.rpan.utils.GeoIpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @auther xiangxiao
 * @email 573768011@qq.com
 * @data 2023/3/21 20:06
 */
@Slf4j
@Service
public class OauthTokenEnhancer implements TokenEnhancer {
  private static final Logger LOG = LogManager.getLogger(OauthTokenEnhancer.class);
  @Autowired
  private UserService userService;
  @Autowired
  private IRoleService iRoleService;
  @Autowired
  private UserMapper userMapper;
  @Autowired
  private HttpServletRequest request;
  @Value("${token.expireTime}")
  private Long tokenExpireTime;
  @Autowired
  private StringRedisTemplate redisTemplate;

  @Override
  public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
    final Map<String, Object> additionalInfo = new HashMap<>(16);

    String login = userDetails.getUsername();
    UserVo user = userService.findUserByAccount(login);
    user.setLoginTimes(user.getLoginTimes() + 1);
    // 更新用户登录次数
    User userDto = new User();
    userDto.setId(user.getId());
    userDto.setLoginTimes(user.getLoginTimes());
    userMapper.updateByPrimaryKeySelective(userDto);
    // 计算用户的密码使用天数
    user.setPasswordUseDay(DateUtil.diffDateCurrDay(user.getPasswordUpdateTime()));

    List<UserRoleInfo> roleInfoOfUser = iRoleService.findRoleInfoOfUser(user.getId(), 0);
    // 用户的权限信息
    UserInfo userInfo = new UserInfo();
    userInfo.setUserVo(user);
    userInfo.setUserRoleInfos(roleInfoOfUser);
    List<Operation> operationList = iRoleService.findAllOperationListByUserId(user.getId());
    userInfo.setOperationList(operationList);

    additionalInfo.put(GlobalConsts.USER_INFO_KEY, userInfo);
    additionalInfo.put(GlobalConsts.USER_ROLE_INFO_KEY, roleInfoOfUser);
    List<Long> moduleIds = iRoleService.findModuleIdByUserId(user.getId());
    Set<Long> roleIds = roleInfoOfUser.stream().map(UserRoleInfo::getRoleId).collect(Collectors.toSet());
    List<AuthModuleInfo> authorityInfoList = iRoleService.findAuthorityByUserIds(user.getId(), moduleIds);
    List<TreeNode> treeNodeList = iRoleService.findAuthorityTree(authorityInfoList, moduleIds, roleIds);
    additionalInfo.put(GlobalConsts.USER_OAUTH_RESOURCE_INFO_KEY, treeNodeList);

    ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
    if (this.tokenExpireTime > 0) {
      ((DefaultOAuth2AccessToken) accessToken).setExpiration(new Date(System.currentTimeMillis() + tokenExpireTime));
    }
    if (this.tokenExpireTime < 0) {
      ((DefaultOAuth2AccessToken) accessToken).setExpiration(new Date(System.currentTimeMillis() + GlobalConsts.DEFAULT_USER_INFO_EXPIRE_TIME));
    }

    // 缓存用户信息
    String token = accessToken.getValue();
    userInfo.setIp(GeoIpUtil.getIpAddress(request));
    String data = JSONObject.toJSONString(userInfo);

    ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
    Boolean handle = valueOperations.setIfAbsent(token, data);
    log.info("存储用户登录信息data={}", data);
    log.info("存储用户登录信息token={},success={}", token, handle);
    if (handle) {
      // 存储用户和token的关系
      OAuth2Request authorizationRequest = authentication.getOAuth2Request();
      String key = login + "_" + 0L + "_" + authorizationRequest.getClientId();
      valueOperations.set(key, token, tokenExpireTime, TimeUnit.MILLISECONDS);
      // 存储token对应的用户信息
      valueOperations.getOperations().expire(token, tokenExpireTime, TimeUnit.MILLISECONDS);
    }
    return accessToken;
  }
}
